package com.qianxun.spider.util.scheduler;

import lombok.Data;
import lombok.experimental.Accessors;
import org.quartz.*;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * @author: huwei
 * @date:   2020/3/30 15:04
 * @version:  1.0.0
*/
public class JobUtil{
    /**
     * 获取任务构建者
     * @return
     */
    public static JobBuilder newBuilder(){
        return new JobBuilder();
    }

    /**
     * 获取当前任务的上下文；
     * 注意：该方法用在执行任务 或回调任务的方法体中，其它任何地方使用必然返回null
     * @param jobName 工作名
     * @param group 工作组
     * @return
     */
    public static JobExecutionContext getContext(String jobName ,String group){
        return getContext(new JobKey(jobName ,group));
    }

    /**
     * 获取当前任务的上下文；
     * 注意：该方法用在执行任务 或回调任务的方法体中，其它任何地方使用必然返回null
     * @param jobKey 工作key
     * @return
     */
    public static JobExecutionContext getContext(JobKey jobKey){
        JobBuilder.Task task = JobBuilder.JobTaskHolder.taskMap.get(jobKey);
        return task == null ? null : task.getInnerThreadObjectHolder().getContext();
    }

    /**
     * @author: huwei
     * @date: 2020/3/27 15:29
     * @version: 1.0.0
     */
    public static class JobBuilder {

        private JobKey jobKey;

        private Task task;

        private JobBuilder(){
            task = new Task();
        }

        public JobBuilder withKey(String jobName ,String group){
            return withKey(new JobKey(jobName ,group));
        }

        public JobBuilder withKey(JobKey jobKey){
            this.jobKey = jobKey;
            return this;
        }

        public JobBuilder withJob(NoCallExecuteFunction task){
            this.task.setNoCallExecuteFunction(task);
            return this;
        }

        public<T> JobBuilder withJob(NoCallParameterExecuteFunction<T> task ,T arg){
            this.task.setNoCallParameterExecuteFunction(task).setArg(arg);
            return this;
        }

        public<R> JobBuilder withJob(ExecuteFunction<R> task , CallFunction<R> call){
            this.task.setExecuteFunction(task).setCallFunction(call);
            return this;
        }

        public<T ,R> JobBuilder withJob(ParameterExecuteFunction<T ,R> task , T arg, CallFunction<R> call){
            this.task.setParameterExecuteFunction(task).setArg(arg).setCallFunction(call);
            return this;
        }

        public JobDetail build(){
            if(Objects.isNull(this.jobKey)){
                throw new RuntimeException("job key can not null");
            }
            if((Objects.isNull(this.task.getParameterExecuteFunction()) || Objects.isNull(this.task.getArg()))
                    && Objects.isNull(this.task.getExecuteFunction())
                    && Objects.isNull(this.task.getNoCallExecuteFunction()) && Objects.isNull(this.task.getNoCallParameterExecuteFunction())){
                throw new RuntimeException("execute job can not null");
            }

            JobDetail jobDetail = org.quartz.JobBuilder.newJob(CommonJob.class).withIdentity(this.jobKey).build();
            JobTaskHolder.taskMap.put(this.jobKey, this.task);
            return jobDetail;
        }
        /**
         * 通用的工作类 ,该类必须为公共静态内部类，这样才能够被Quartz反射成功
         */
        public static class CommonJob implements  Job{
            @Override
            public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
                Task task = JobTaskHolder.taskMap.get(jobExecutionContext.getJobDetail().getKey());
                if(task == null){
                    System.err.println("--- 没有" + jobExecutionContext.getJobDetail().getKey() + "所对应到工作");
                    return;
                }

                //任务前置任务
                before(jobExecutionContext ,task);

                try {
                    //任务执行
                    Object res = null;
                    if (Objects.nonNull(task.getParameterExecuteFunction())) {
                        res = task.getParameterExecuteFunction().exe(task.getArg());
                    } else if (Objects.nonNull(task.getExecuteFunction())) {
                        res = task.getExecuteFunction().exe();
                    } else if (Objects.nonNull(task.getNoCallExecuteFunction())) {
                        task.getNoCallExecuteFunction().exe();
                    } else if (Objects.nonNull(task.getNoCallParameterExecuteFunction())) {
                        task.getNoCallParameterExecuteFunction().exe(task.getArg());
                    }

                    //有参回调
                    if (Objects.nonNull(res) && Objects.nonNull(task.getCallFunction())) {
                        task.getCallFunction().call(res);
                    }

                }catch (Throwable throwable){
                    error(jobExecutionContext ,task ,throwable);
                }

                //任务完整之后执行
                after(jobExecutionContext ,task);
            }

            private void before(JobExecutionContext jobExecutionContext ,Task task){
                //创建JobUtil对象，为用户提供工具处理
                task.setInnerThreadObjectHolder(new InnerThreadObjectHolder(jobExecutionContext));
            }

            private void after(JobExecutionContext jobExecutionContext ,Task task){
                //清除job工具，释放资源
                task.getInnerThreadObjectHolder().reset();
                task.setInnerThreadObjectHolder(null);
            }

            private void error(JobExecutionContext jobExecutionContext ,Task task ,Throwable throwable){
                //异常任务
                throwable.printStackTrace();
                throw new RuntimeException("任务执行异常：".concat(throwable.getMessage()) ,throwable);
            }
        }

        @Data
        @Accessors(chain = true)
        private class Task<T ,R> {
            NoCallExecuteFunction noCallExecuteFunction;
            NoCallParameterExecuteFunction<T> noCallParameterExecuteFunction;
            ParameterExecuteFunction<T ,R> parameterExecuteFunction;
            ExecuteFunction<R> executeFunction;
            CallFunction<R> callFunction;
            T arg;

            InnerThreadObjectHolder innerThreadObjectHolder;
        }

        /**
         * 任务持有对象
         */
        private static final class JobTaskHolder {
            static Map<JobKey ,Task> taskMap = new ConcurrentHashMap<>();
        }

        /**
         * 任务工具 ，用于获取原始Quartz方法执行到JobExecutionContext对象，以及MyJobDetail对象
         */
        public static class InnerThreadObjectHolder{
            /**
             * 任务执行时，原生JobExecutionContext对象
             */
            private ThreadLocal<JobExecutionContext> jobExecutionContextThreadLocal = new InheritableThreadLocal<>();

            private InnerThreadObjectHolder(JobExecutionContext jobExecutionContext) {
                this.jobExecutionContextThreadLocal.set(jobExecutionContext);
            }

            public JobExecutionContext getContext() {
                return jobExecutionContextThreadLocal.get();
            }

            private void setContext(JobExecutionContext context){
                jobExecutionContextThreadLocal.set(context);
            }

            private void removeContext(){
                jobExecutionContextThreadLocal.remove();
            }

            private void reset() {
                //释放资源
                jobExecutionContextThreadLocal.remove();
            }
        }
    }
}
